package com.miukoo.aikao.qpaper.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.miukoo.aikao.core.dtos.ExamDto;
import com.miukoo.aikao.core.dtos.PageReqDto;
import com.miukoo.aikao.core.enums.CodingType;
import com.miukoo.aikao.core.enums.ExamState;
import com.miukoo.aikao.core.enums.PaperState;
import com.miukoo.aikao.core.enums.QuestionType;
import com.miukoo.aikao.core.exceptions.ApiException;
import com.miukoo.aikao.core.pojos.Question;
import com.miukoo.aikao.core.pojos.QuestionAnswer;
import com.miukoo.aikao.core.pojos.QuestionChooseDto;
import com.miukoo.aikao.core.utils.BeanMapperUtils;
import com.miukoo.aikao.metadata.api.v1.SysUserApi;
import com.miukoo.aikao.metadata.pojos.SysUser;
import com.miukoo.aikao.qexam.api.v1.ExamApi;
import com.miukoo.aikao.qexam.api.v1.ExamRepoApi;
import com.miukoo.aikao.qmanage.api.v1.QuestionAnswerApi;
import com.miukoo.aikao.qmanage.api.v1.QuestionApi;
import com.miukoo.aikao.qpaper.choose.JoinTypeInterfaceManager;
import com.miukoo.aikao.qpaper.dto.PaperQuestionDto;
import com.miukoo.aikao.qpaper.dto.ext.PaperQuestionAnswerExtDto;
import com.miukoo.aikao.qpaper.dto.ext.PaperQuestionDetailDto;
import com.miukoo.aikao.qpaper.dto.request.PaperAnswerDto;
import com.miukoo.aikao.qpaper.dto.request.PaperListReqDto;
import com.miukoo.aikao.qpaper.dto.response.ExamDetailRespDto;
import com.miukoo.aikao.qpaper.dto.response.ExamResultRespDto;
import com.miukoo.aikao.qpaper.dto.response.PaperListRespDto;
import com.miukoo.aikao.qpaper.mapper.PaperMapper;
import com.miukoo.aikao.qpaper.pojos.Paper;
import com.miukoo.aikao.qpaper.pojos.PaperQuestion;
import com.miukoo.aikao.qpaper.pojos.PaperQuestionAnswer;
import com.miukoo.aikao.qpaper.service.PaperQuestionAnswerService;
import com.miukoo.aikao.qpaper.service.PaperQuestionService;
import com.miukoo.aikao.qpaper.service.PaperService;
import com.miukoo.aikao.ucenter.api.v1.AchievementApi;
import com.miukoo.aikao.ucenter.api.v1.WrongQuestionApi;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;

/**
* <p>
* 语言设置 服务实现类
* </p>
*
* @author 聪明笨狗
* @since 2020-05-25 16:33
*/
@Service
@Transactional
public class PaperServiceImpl extends ServiceImpl<PaperMapper, Paper> implements PaperService {


    @Autowired
    private SysUserApi sysUserApi;

    @Autowired
    private ExamApi examApi;

    @Autowired
    private QuestionApi questionApi;

    @Autowired
    private QuestionAnswerApi questionAnswerApi;

    @Autowired
    private PaperService paperService;

    @Autowired
    private PaperQuestionService paperQuestionService;

    @Autowired
    private PaperQuestionAnswerService paperQuAnswerService;

    @Autowired
    private WrongQuestionApi wrongQuestionApi;

    @Autowired
    private ExamRepoApi examRepoApi;

    @Autowired
    private AchievementApi achievementApi;

    @Autowired
    private JoinTypeInterfaceManager joinTypeInterfaceManager;

    /**
     * 展示的选项，ABC这样
     */
    private static List<String> ABC = Arrays.asList(new String[]{
            "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K","L","M","N","O","P","Q","R","S","T","U","V","W","X"
            ,"Y","Z"
    });





    @Override
    public Long createPaper(Long userId, String userName, Long examId) {

        // 查找考试
        ExamDto exam = examApi.findById(examId);

        if(exam == null){
            throw new ApiException(1, "考试不存在！");
        }

        if(!ExamState.ENABLE.equals(exam.getState())){
            throw new ApiException(1, "考试状态不正确！");
        }

        // 按题库组卷
        List<QuestionChooseDto> questionChooseDtos = joinTypeInterfaceManager.chooseQuestion(exam);

        //保存试卷内容
        Long paperId = this.savePaper(userId, userName, exam, questionChooseDtos);

        return paperId;
    }

    @Override
    public ExamDetailRespDto paperDetail(Long paperId) {
        ExamDetailRespDto respDto = new ExamDetailRespDto();

        // 试题基本信息
        Paper paper = paperService.getById(paperId);
        BeanMapperUtils.copy(paper, respDto);

        // 查找题目列表
        List<PaperQuestionDto> list = paperQuestionService.listByPaper(paperId);

        List<PaperQuestionDto> radioList = new ArrayList<>();
        List<PaperQuestionDto> multiList = new ArrayList<>();
        List<PaperQuestionDto> judgeList = new ArrayList<>();
        List<PaperQuestionDto> qaList = new ArrayList<>();
        List<PaperQuestionDto> codingList = new ArrayList<>();
        for(PaperQuestionDto item: list){
            if(QuestionType.RADIO.equals(item.getQuestionType())){
                radioList.add(item);
            }
            if(QuestionType.MULTI.equals(item.getQuestionType())){
                multiList.add(item);
            }
            if(QuestionType.JUDGE.equals(item.getQuestionType())){
                judgeList.add(item);
            }
            if(QuestionType.QA.equals(item.getQuestionType())){
                qaList.add(item);
            }
            if(QuestionType.CODING.equals(item.getQuestionType())){
                codingList.add(item);
            }
        }
        respDto.setRadioList(radioList);
        respDto.setMultiList(multiList);
        respDto.setJudgeList(judgeList);
        respDto.setQaList(qaList);
        respDto.setCodingList(codingList);
        return respDto;
    }

    @Override
    public ExamResultRespDto paperResult(Long paperId, boolean isAdmin) {
        ExamResultRespDto respDto = new ExamResultRespDto();
        // 试题基本信息
        Paper paper = paperService.getById(paperId);
        BeanMapperUtils.copy(paper, respDto);
        if(isAdmin || PaperState.FINISHED>=paper.getState()) {
            List<PaperQuestionDetailDto> quList = paperQuestionService.listForPaperResult(paperId);
            respDto.setQuList(quList);
        }else{
            respDto.setQaScore((short)0);
            respDto.setQualifyScore((short)0);
            respDto.setRadioScore((short)0);
            respDto.setUserScore((short)0);
            respDto.setTotalScore((short)0);
        }
        return respDto;
    }

    @Override
    public PaperQuestionDetailDto findQuDetail(Long paperId, Long quId) {

        PaperQuestionDetailDto respDto = new PaperQuestionDetailDto();
        // 问题
        Question qu = questionApi.findById(quId);

        // 基本信息
        PaperQuestion paperQu = paperQuestionService.findByKey(paperId, quId);
        BeanMapperUtils.copy(paperQu, respDto);
        respDto.setContent(qu.getContent());
        respDto.setRemark(qu.getRemark());
        respDto.setCoding(qu.getCoding());
        respDto.setCodingType(CodingType.getType(qu.getCodingType()).getType());
        respDto.setCodingUploadType(qu.getCodingUploadType());

        // 答案列表
        List<PaperQuestionAnswerExtDto> list = paperQuAnswerService.listForExam(paperId, quId);
        respDto.setAnswerList(list);

        return respDto;
    }

    /**
     * 保存试卷
     * @param userId
     * @param exam
     * @param questionChooseDtos
     * @return
     */
    private Long savePaper(Long userId, String userName, ExamDto exam, List<QuestionChooseDto> questionChooseDtos) {
        // 查找用户
        SysUser user = sysUserApi.getByUserId(userId);

        //保存试卷基本信息
        Paper paper = new Paper();
        paper.setDepartId(user.getDepartId());
        paper.setExamId(exam.getId());
        paper.setTitle(exam.getTitle());
        paper.setUsername(userName);
        paper.setTotalScore(exam.getTotalScore());
        paper.setTotalTime(exam.getTotalTime());
        paper.setUserScore((short)0);
        paper.setUserId(userId);
        paper.setCreateTime(new Date());
        paper.setUpdateTime(new Date());
        paper.setQualifyScore(exam.getQualifyScore());
        paper.setState(PaperState.ING);
        paper.setHasSaq(false);

        // 截止时间
        Calendar cl = Calendar.getInstance();
        cl.setTimeInMillis(System.currentTimeMillis());
        cl.add(Calendar.MINUTE, exam.getTotalTime());
        paper.setLimitTime(cl.getTime());

        paperService.save(paper);

        if (!CollectionUtils.isEmpty(questionChooseDtos)) {
            int qaCount = this.savePaperQu(paper.getId(), questionChooseDtos);
            if(qaCount>0){
                paper.setHasSaq(true);
                updateById(paper);
            }
        }

        return paper.getId();
    }



    /**
     * 保存试卷试题列表
     * @param paperId
     * @param questionChooseDtos
     */
    private int savePaperQu(Long paperId, List<QuestionChooseDto> questionChooseDtos){
        List<PaperQuestion> batchQuList = new ArrayList<>();
        List<PaperQuestionAnswer> batchAnswerList = new ArrayList<>();
        int sort = 0;
        int qaCount=0;
        for (QuestionChooseDto dto : questionChooseDtos) {
            PaperQuestion item = new PaperQuestion();
            item.setPaperId(paperId);
            item.setSort(sort);
            item.setId(Long.valueOf(IdWorker.getIdStr()));
            item.setScore(dto.getScore());
            item.setQuestionId(dto.getId());
            item.setQuestionType(dto.getType());
            item.setAnswered(false);
            item.setIsRight(false);
            item.setAnswer("");
            item.setActualScore(dto.getScore());
            if(item.getQuestionType()>99){
                qaCount++;
            }
            //回答列表
            List<QuestionAnswer> answerList = dto.getAnswers();
            if (!CollectionUtils.isEmpty(answerList)) {
                int ii = 0;
                boolean isAbc = answerList.size()>ABC.size();
                for (QuestionAnswer answer : answerList) {
                    PaperQuestionAnswer paperQuestionAnswer = new PaperQuestionAnswer();
                    paperQuestionAnswer.setId(Long.valueOf(IdWorker.getIdStr()));
                    paperQuestionAnswer.setPaperId(paperId);
                    paperQuestionAnswer.setQuestionId(answer.getQuId());
                    paperQuestionAnswer.setAnswerId(answer.getId());
                    paperQuestionAnswer.setChecked(false);
                    paperQuestionAnswer.setSort(ii);
                    if(isAbc){
                        paperQuestionAnswer.setAbc(""+(ii+1));
                    }else {
                        paperQuestionAnswer.setAbc(ABC.get(ii));
                    }
                    paperQuestionAnswer.setIsRight(answer.getIsRight());
                    ii++;
                    batchAnswerList.add(paperQuestionAnswer);
                }
            }
            batchQuList.add(item);
            sort++;
        }

        //添加问题
        paperQuestionService.saveBatch(batchQuList);

        //批量添加问题答案
        paperQuAnswerService.saveBatch(batchAnswerList);

        return qaCount;
    }

    @Override
    public void fillAnswer(PaperAnswerDto reqDto) {
        // 未作答
        if(CollectionUtils.isEmpty(reqDto.getAnswers())
                && StringUtils.isBlank(reqDto.getAnswer())){
            return;
        }
        //查找答案列表
        List<PaperQuestionAnswer> list = paperQuAnswerService.listForFill(reqDto.getPaperId(), reqDto.getQuestionId());

        //是否正确
        boolean right = true;

        //更新正确答案
        for (PaperQuestionAnswer item : list) {
            if (reqDto.getAnswers().contains(item.getId().toString())) {
                item.setChecked(true);
            } else {
                item.setChecked(false);
            }
            //有一个对不上就是错的
            if (item.getIsRight()!=null && !item.getIsRight().equals(item.getChecked())) {
                right = false;
            }
            paperQuAnswerService.updateById(item);
        }

        //修改为已回答
        PaperQuestion qu = new PaperQuestion();
        qu.setQuestionId(reqDto.getQuestionId());
        qu.setPaperId(reqDto.getPaperId());
        qu.setIsRight(right);
        qu.setAnswer(reqDto.getAnswer());
        qu.setAnswered(true);

        paperQuestionService.updateByKey(qu);

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void handExam(Long paperId,Long userId,String userName) {

        //获取试卷信息
        Paper paper = paperService.getById(paperId);

        //如果不是正常的，抛出异常
        if(!PaperState.ING.equals(paper.getState())){
            throw new ApiException(1, "试卷状态不正确！");
        }

        // 客观分
        Short objScore = (short)paperQuestionService.sumRadioScore(paperId);
        paper.setRadioScore(objScore);
        paper.setUserScore(objScore);

        // 主观分，因为要阅卷，所以给0
        paper.setQaScore((short)0);

        // 待阅卷
        if(paper.getHasSaq()) {
            paper.setState(PaperState.WAIT_OPT);
        }else {
            // 同步保存考试成绩
            achievementApi.joinResult(userId,userName,paper.getTitle(), paper.getExamId(), objScore, objScore>=paper.getQualifyScore());
            paper.setState(PaperState.FINISHED);
        }
        paper.setUpdateTime(new Date());

        //计算考试时长
        Calendar cl = Calendar.getInstance();
        cl.setTimeInMillis(System.currentTimeMillis());
        short userTime = (short)((System.currentTimeMillis() - paper.getCreateTime().getTime()) / 1000 / 60);
        if(userTime == 0){
            userTime = 1;
        }
        paper.setUserTime(userTime);

        //更新试卷
        paperService.updateById(paper);

        if(paper.getState() == PaperState.FINISHED.shortValue()) {
            //把打错的问题加入错题本
            List<PaperQuestionDto> list = paperQuestionService.listByPaper(paperId);
            for (PaperQuestionDto qu : list) {
                // 主观题和对的都不加入错题库
                if (qu.getIsRight()) {
                    continue;
                }
                //加入错题本
                wrongQuestionApi.addWrongQuestion(paper.getExamId(), qu.getQuestionId(), paper.getUserId());
            }
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void reviewPaper(ExamResultRespDto reqDto) {
        Paper paper = this.getById(reqDto.getId());
        if(paper!=null&&paper.getState()==PaperState.FINISHED.shortValue()){
            throw new ApiException(1,"不能批阅已经结束的试卷！");
        }
        List<PaperQuestionDetailDto> quList = reqDto.getQuList();
        List<PaperQuestion> list = new ArrayList<>();
        for(PaperQuestionDetailDto item: quList){
            PaperQuestion qu = new PaperQuestion();
            qu.setId(item.getId());
            qu.setActualScore(item.getActualScore());
            qu.setIsRight(item.getIsRight());
            list.add(qu);
        }

        // 批量修改
        paperQuestionService.updateBatchById(list);

        // 主观分
        short sumQaScore = (short)paperQuestionService.sumQaScore(reqDto.getId());
        // 客观分
        short sumRadioScore = (short)paperQuestionService.sumRadioScore(reqDto.getId());

        // 修改试卷状态
        paper.setQaScore(sumQaScore);
        paper.setRadioScore(sumRadioScore);
        paper.setUserScore((short)(sumQaScore + sumRadioScore));
        paper.setState(PaperState.FINISHED);
        paper.setUpdateTime(new Date());
        this.updateById(paper);

        // 同步保存考试成绩
        achievementApi.joinResult( paper.getUserId(), paper.getUsername(),paper.getTitle(), paper.getExamId(), paper.getUserScore(), paper.getUserScore()>=paper.getQualifyScore());

        //把打错的问题加入错题本
        List<PaperQuestionDto> pqList = paperQuestionService.listByPaper(paper.getId());
        for (PaperQuestionDto qu : pqList) {
            // 主观题和对的都不加入错题库
            if (qu.getIsRight()) {
                continue;
            }
            //加入错题本
            wrongQuestionApi.addWrongQuestion(paper.getExamId(), qu.getQuestionId(), paper.getUserId());
        }
    }

    @Override
    public IPage<PaperListRespDto> paging(PageReqDto<PaperListReqDto> reqDto) {
        return baseMapper.paging(reqDto.toPage(), reqDto.getParams());
    }

    @Override
    public List<Paper> findDeadPapers() {

        // 结束后两分钟，非正常交卷
        Calendar cl = Calendar.getInstance();
        cl.setTimeInMillis(System.currentTimeMillis());
        cl.add(Calendar.MINUTE, -5);

        QueryWrapper<Paper> wrapper = new QueryWrapper<>();
        wrapper.lambda()
                .le(Paper::getLimitTime, cl.getTime())
                .eq(Paper::getState, PaperState.ING);


        return this.list(wrapper);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void breakExam(Long paperId) {
        Paper paper = new Paper();
        paper.setId(paperId);
        paper.setState(PaperState.BREAK);
        paper.setUpdateTime(new Date());
        this.updateById(paper);
    }
}
